/*
 * @Description: 模型漫游工具
 * @Author: your name
 * @version:
 * @Date: 2024-12-05 14:06:11
 * @LastEditors: your name
 * @LastEditTime: 2024-12-06 09:09:11
 * @usage
 * new roam=new RoamTool(viewer,{
 *  modelUrl,
 *  cameraView:'自由视角' | '绑定视角' | '上帝视角',
 *  speed:10,
 *  isLoop:true
 * })
 * 开始漫游
 * roam.startRoam(positions)
 * 暂停漫游
 * roam.stopRoam()
 * 销毁
 * roam.destroyRoam()
 */
import * as Cesium from 'cesium'

class RoamTool {
  /*
   * 漫游工具
   * @param {Cesium.Viewer} viewer : viewer程序对象
   * @param {String} modelUrl:漫游模型的路径
   * @param {String} cameraView:相机视角:'自由视角' | '第三人称' | '上帝视角'
   * @param {Number} speed:漫游速度
   * @param {Boolean} isLoop:是否循环播放漫游
   */
  constructor(viewer, options = {}) {
    const defaultOptions = {
      modelUrl: '/src/assets/gltf/xiaofangche.gltf',
      cameraView: '自由视角',
      speed: 10,
      isLoop: true,
    }
    this.viewer = viewer
    this.options = Object.assign(defaultOptions, options)
    this.roamEntity = null
  }

  /*
   * 开始漫游
   * @param {Array<Cesium.Cartesian3>} positions : 漫游路线数组
   */
  startRoam(positions) {
    const { modelUrl, isLoop } = this.options
    // 这个要先设置为true，否则时间不会动
    this.viewer.clock.shouldAnimate = true
    // 创建开始漫游的时间点
    const startTime = Cesium.JulianDate.fromDate(new Date())
    // 通过坐标positions和速度，计算每个坐标的时间点以及总时间
    const { totalTime, siteTimes } = this.getSiteTime(positions)
    const stopTime = Cesium.JulianDate.addSeconds(
      startTime,
      totalTime,
      new Cesium.JulianDate()
    )
    // 设置当前的时钟时间为startTime
    this.viewer.clock.currentTime = startTime
    this.viewer.clock.startTime = startTime
    this.viewer.clock.stopTime = stopTime
    // 是否在startTime和stopTime之间循环
    this.viewer.clock.clockRange = isLoop
      ? Cesium.ClockRange.LOOP_STOP
      : Cesium.ClockRange.UNBOUNDED

    // 创建availability,时间可见性
    const availability = new Cesium.TimeIntervalCollection([
      new Cesium.TimeInterval({
        start: startTime,
        stop: stopTime,
      }),
    ])

    // 构造sampledProperty
    const sampledProperty = this.getSampledProperty(
      siteTimes,
      positions,
      startTime
    )
    // 小车朝向设置 VelocityOrientationProperty一般和sampledProperty绑定，用来调整朝向角
    const orientation = new Cesium.VelocityOrientationProperty(sampledProperty)

    this.roamEntity = this.viewer.entities.add({
      name: 'roamCar',
      availability,
      position: sampledProperty,
      orientation,
      model: {
        uri: modelUrl,
        scale: 1,
      },
      // 展示漫游路径
      path: {
        show: true,
        material: new Cesium.PolylineGlowMaterialProperty({
          glowPower: 0.5,
          color: Cesium.Color.YELLOWGREEN,
        }),
        width: 10,
      },
    })
    // 根据cameraView设置视角
    this.cameraSet()
  }

  // 通过坐标和速度，计算时间戳
  getSiteTime(positions) {
    const { speed } = this.options
    // 记录时间戳
    const times = []
    // 记录总时间
    let timeSum = 0
    for (let i = 0; i < positions.length; i++) {
      if (i === 0) {
        times.push(0)
        continue
      }
      const lastPosition = positions[i - 1]
      // 计算上一个时间点和当前循环的时间点的距离
      const dis = Cesium.Cartesian3.distance(lastPosition, positions[i])
      const timeBreak = dis / speed
      timeSum += Number(timeBreak)
      times.push(timeSum)
    }
    return {
      totalTime: timeSum,
      siteTimes: times,
    }
  }

  // 创建sampledProperty
  getSampledProperty(siteTimes, positions, startTime) {
    const sampledProperty = new Cesium.SampledPositionProperty()
    // 因为时间戳和值是一一对应的，所以时间戳和坐标长度要相等
    if (siteTimes.length !== positions.length) {
      return
    }
    positions.forEach((position, index) => {
      const time = siteTimes[index]
      const JulianDate = new Cesium.JulianDate.addSeconds(
        startTime,
        time,
        new Cesium.JulianDate()
      )
      sampledProperty.addSample(JulianDate, position)
    })
    return sampledProperty
  }

  cameraSet() {
    const { cameraView } = this.options
    this.viewer.trackedEntity = null
    // 解除相机的矩阵绑定
    this.viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
    if (cameraView === '自由视角') {
      this.viewer.trackedEntity = this.roamEntity
    } else {
      // 监听时钟的事件
      this.viewer.clock.onTick.addEventListener(this.trackEvent, this)
    }
  }

  trackEvent() {
    const { cameraView } = this.options
    let offset
    if (cameraView === '第三人称') {
      offset = new Cesium.Cartesian3(-100, 0, 50)
    } else if (cameraView === '第一人称') {
      offset = new Cesium.Cartesian3(0, 10, 10)
    } else {
      offset = new Cesium.Cartesian3(0, 0, 100)
    }
    // 根据时间，获取当前的小车位置
    const position = this.roamEntity.position.getValue(
      this.viewer.clock.currentTime
    )
    // 根据时间，获取小车朝向,orientation是一个四元数
    const orientation = this.roamEntity.orientation.getValue(
      this.viewer.clock.currentTime
    )
    const tranform = Cesium.Matrix4.fromRotationTranslation(
      // 从一个四元数，转为旋转矩阵
      Cesium.Matrix3.fromQuaternion(orientation),
      position
    )

    // lookAtTransform可以让相机绑定小车的位置和朝向
    this.viewer.camera.lookAtTransform(tranform, offset)
  }

  stopRoam() {
    // 暂停漫游，就是暂停时间
    this.viewer.clock.shouldAnimate = false
  }

  destroyRoam() {
    this.roamEntity && this.viewer.entities.remove(this.roamEntity)
    this.roamEntity = null
    this.viewer.trackedEntity = null
    this.viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
    this.viewer.clock.onTick.removeEventListener(this.trackEvent, this)
  }
}

export default RoamTool
